home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Risc World 3
/
Risc World 3.iso
/
SOFTWARE
/
ISSUE6
/
PD
/
PDF
/
pdf
/
c++
/
outline
< prev
next >
Wrap
Text File
|
2003-02-14
|
16KB
|
599 lines
//--------------------------------------------------------------------------
//
// Copyright (c) 2002, Colin Granville
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or
// without modification, are permitted provided that the following
// conditions are met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// * The name Colin Granville may not be used to endorse or promote
// products derived from this software without specific prior
// written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//
//--------------------------------------------------------------------------
#include "guilib:gfx.h"
#include "outline.h"
#include "PDFDoc.h"
#include "Catalog.h"
#include "iostream.h"
#include <stdlib.h>
#include <stdarg.h>
#include "object.h"
#include "xref.h"
#include "link.h"
#include "stl:string.h"
#include "DocView.h"
#include "GuiIcon.h"
#include "GuiTask.h"
#include "Document.h"
#include "GuiHourglass.h"
#include "DrawOutputFont.h"
#define SPRITEOUTDENT 28
inline int levelIndent(int level) {return level*52;}
LinkAction* makeAction(Dict *dict, GString *baseURI);
class OutlineFont
{
public:
OutlineFont();
~OutlineFont();
bool isOk() {return handle;}
private:
int handle;
};
//******************************************************************************
OutlineFont::OutlineFont()
: handle(0)
{
_swix(Font_FindFont,_INR(1,5)|_OUT(0),"Homerton.Medium",10*16,10*16,0,0,&handle);
}
//******************************************************************************
OutlineFont::~OutlineFont()
{
if (handle) _swix(Font_LoseFont,_IN(0),handle);
}
//******************************************************************************
//******************************************************************************
//******************************************************************************
class OutlineNode
{
public:
OutlineNode(OutlineNode* parent_,Object* o);
~OutlineNode();
OutlineNode* getParent() {return parent;}
void setParent(OutlineNode* n) {parent=n;}
OutlineNode* getNext() {return next;}
void setNext(OutlineNode* n) {next=n;}
OutlineNode* getFirst() {return first;}
void setFirst(OutlineNode* n) {first=n;}
int getCount() {return count;}
void setCount(int n) {count=n;}
const string& getText() const {return text;}
Object* getObject() {return ob;}
private:
OutlineNode* parent;
OutlineNode* first;
OutlineNode* next;
int count;
Object* ob;
string text;
};
OutlineNode::OutlineNode(OutlineNode* parent_,Object* o)
: parent(parent_),
first(0),
next(0),
count(0),
ob(o)
{
Object t;
o->dictLookup("Title",&t);
if (t.isString()) text=toAcornLatin1(t.getString()->getCString(),
t.getString()->getLength());
t.free();
}
//******************************************************************************
OutlineNode::~OutlineNode()
{
delete first;
delete next;
if (ob) ob->free();
delete ob;
}
//******************************************************************************
//******************************************************************************
//******************************************************************************
Outline::Outline(PDFDoc& doc)
: window("Outline"),
owner(0),
root(0),
pdfDoc(0),
height(28),
redrawWindowTarget(&window,GuiWimp_ERedrawWindow,this,Outline::redrawWindow),
mouseClickTarget(&window,GuiWimp_EMouseClick,this,Outline::mouseClick)
{
if (!doc.getXRef()) return;
Object catalog;
doc.getXRef()->getCatalog(&catalog);
if (!catalog.isDict()) {catalog.free();return;}
Object outlines;
catalog.dictLookup("Outlines",&outlines);
if (outlines.isDict())
{
Object ob;
int count=0;
outlines.dictLookup("First",&ob);
if (ob.isDict()) count++;
ob.free();
outlines.dictLookup("Next",&ob);
if (ob.isDict()) count++;
ob.free();
if (count>0) pdfDoc=&doc;
}
outlines.free();
catalog.free();
}
//******************************************************************************
Outline::~Outline()
{
delete root;
}
//******************************************************************************
OutlineNode* Outline::fillTree(OutlineNode*parent,Object* entry )
{
if (!entry) return 0;
OutlineNode* node = new OutlineNode(parent,entry);
if (!node) return 0;
Object* nextOb=new Object;
if (nextOb)
{
entry->dictLookup("Next",nextOb);
if (!nextOb->isDict())
{
nextOb->free();
delete nextOb;
nextOb=0;
}
}
Object* firstOb=new Object;
if (firstOb)
{
entry->dictLookup("First",firstOb);
if (!firstOb->isDict())
{
firstOb->free();
delete firstOb;
firstOb=0;
}
}
int count=0;
{
Object ob;
entry->dictLookup("Count",&ob);
if (ob.isInt()) count=ob.getInt();
ob.free();
}
node->setCount(count);
node->setFirst(fillTree(node,firstOb));
node->setNext(fillTree(parent,nextOb));
return node;
}
//******************************************************************************
// Font must be set before use
bool Outline::print(OutlineNode* node,int level)
{
while (node)
{
if (node->getText().size())
{
basey-=height;
int y=redrawBlock->yToScreen(basey);
if (y+height <= redrawBlock->redrawArea.ymin) return 1;
if (y < redrawBlock->redrawArea.ymax)
{
OutlineNode*n=node;
gfx::gcol_bgr(0,0xdadada00);
int x=redrawBlock->xToScreen(basex);
gfx::move(x+levelIndent(level)-SPRITEOUTDENT+8,y+height/2);
gfx::drawby(SPRITEOUTDENT-12,0);
for (int i=level;i>0;i--,n=n->getParent())
{
int extra=(n->getNext()?0:height/2);
if (extra && i!=level) continue;
gfx::move(x+levelIndent(i)-SPRITEOUTDENT+8,y+extra);
gfx::drawby(0,height-extra);
}
int left=basex+levelIndent(level);
if (node->getCount())
{
GuiIcon icon;
icon.flags = GuiIcon::Sprite |
GuiIcon::Indirected;
icon.data.is.spriteNameLength = 12;
icon.data.is.spriteArea = (void*) guiTask().spriteArea();
icon.data.is.sprite = (node->getCount()<0 ? "plus":"minus");
icon.bbox(left-SPRITEOUTDENT,basey+height/4,left,basey+height+height/4);
icon.plot();
}
_swix(ColourTrans_SetFontColours,_INR(0,3),0,0xFFFFFF00,0,14);
_swix(Font_Paint,_INR(0,7),0,node->getText().c_str(),0,
redrawBlock->xToScreen(left)*400,y*400+height*400*1/4,0,0,0);
}
}
if (node->getCount() > 0) if (print(node->getFirst(),level+1)) return 1;
node=node->getNext();
}
return 0;
}
//******************************************************************************
bool Outline::find(OutlineNode* node,int level)
{
for (;node;node=node->getNext())
{
if (node->getText().size())
{
basey-=height;
if (findy >=basey && findy < basey+height )
{
foundNode=node;
foundLevel=level;
return 1;
}
}
if (node->getCount() > 0 && find(node->getFirst(),level+1)) return 1;
}
return 0;
}
//******************************************************************************
int Outline::countLines(OutlineNode* node)
{
int count=0;
for (;node;node=node->getNext())
{
if (node->getText().size()) count++;
if (node->getCount() > 0) count+=countLines(node->getFirst());
}
return count;
}
//******************************************************************************
// returned width is in millipoints
// Font must be set before use
int getMaxWidth(OutlineNode* node,int level=0)
{
int width=0;
for (;node;node=node->getNext())
{
if (node->getText().size())
{
int wid;
_swix(Font_ScanString,_INR(1,4) | _OUT(3),node->getText().c_str(),0,0x80000000,0x80000000,&wid);
wid+=(levelIndent(level)*400);
if (wid>width) width=wid;
}
if (node->getCount() != 0)
{
int wid=getMaxWidth(node->getFirst(),level+1);
if (wid>width) width=wid;
}
}
return width;
}
//******************************************************************************
void Outline::open(DocView& view)
{
if (owner!=&view) close(view);
owner=&view;
if (!isOk()) return;
if (!root)
{
Object catalog;
pdfDoc->getXRef()->getCatalog(&catalog);
if (!catalog.isDict()) {catalog.free();return;}
Object* outlines=new Object;
if (outlines)
{
catalog.dictLookup("Outlines",outlines);
if (!outlines->isDict())
{
outlines->free();
delete outlines;
outlines=0;
}
}
catalog.free();
{
GuiHourglass hg;
root=fillTree(0,outlines);
}
if (!root)
{
if (outlines) outlines->free();
delete outlines;
}
else
{
GuiBBox box;
window.getExtent(box);
int ext=countLines(root)*height;
if (ext==0)
{
delete root;
root=0;
}
else
{
box.ymin=-ext;
OutlineFont font;
box.xmax=(getMaxWidth(root)/400)+16;
window.setExtent(box);
}
}
if (!root) pdfDoc=0;
}
if (isOk()) window.showAsMenuAtPointer(view.getWindow());
}
//******************************************************************************
void Outline::close(DocView& view)
{
if (owner==&view)
{
window.hide();
owner=0;
}
}
//******************************************************************************
Claim Outline::redrawWindow(GuiWimpPollBlock& wpb,const GuiIdBlock&)
{
bool more;
GuiRedrawWindowBlock& block=wpb.redrawWindowRequest;
for (GuiWindow::redraw(block,more);more;GuiWindow::getRectangle(block,more))
{
basey=0;
basex=0;
redrawBlock=█
OutlineFont font;
if (font.isOk()) print(root);
}
return CLAIM;
}
//*************************************************************************
Claim Outline::mouseClick(GuiWimpPollBlock& wpb,const GuiIdBlock&)
{
if (wpb.mouseClick.buttons & GuiPointerInfo::Menu) return CLAIM;
GuiGetWindowStateBlock ws;
window.getState(ws);
int x=ws.xToWorkarea(wpb.mouseClick.x);
findy=ws.yToWorkarea(wpb.mouseClick.y);
basey=0;
if (find(root))
{
if (foundNode->getCount())
{
int x=ws.xToWorkarea(wpb.mouseClick.x);
if (x >= levelIndent(foundLevel)-SPRITEOUTDENT-4 &&
x < levelIndent(foundLevel))
{
foundNode->setCount(-foundNode->getCount());
if (wpb.mouseClick.buttons & GuiPointerInfo::Adjust)
{
int close=(foundNode->getCount()<0);
while (foundNode->getNext())
{
foundNode=foundNode->getNext();
if ((close && foundNode->getCount()>0) ||
(!close && foundNode->getCount()<0)
) foundNode->setCount(-foundNode->getCount());
}
}
GuiBBox box;
window.getExtent(box);
int ext=countLines(root)*height;
if (ext<ws.visibleArea.getHeight()) ext=ws.visibleArea.getHeight();
box.ymin=-ext;
window.setExtent(box);
int bottom=-ws.yToWorkarea(ws.visibleArea.ymin);
if (bottom>ext)
{
ws.yscroll+=(bottom-ext);
window.showAsMenu(ws,owner->getWindow());
}
box.ymax=basey+height;
window.forceRedraw(box);
return CLAIM;
}
}
{ // limit clicks to text
int wid=0;
OutlineFont font;
if (font.isOk()) _swix(Font_ScanString,_INR(0,5) | _OUT(3), 0,foundNode->getText().c_str(),0,
-1,-1,0,&wid);
if (x < levelIndent(foundLevel) || x > (levelIndent(foundLevel)+wid/400))
{
if (owner) close(*owner);
return CLAIM;
}
}
if (owner)
{
LinkAction* action=makeAction(foundNode->getObject()->getDict(),
owner->getDocument().getCatalog()->getBaseURI());
if (action)
{
owner->doAction(action,wpb.mouseClick.buttons & GuiPointerInfo::Adjust);
delete action;
}
close(*owner);
}
}
else
{
if (owner) close(*owner);
}
return CLAIM;
}
//******************************************************************************
//******************************************************************************
//******************************************************************************
LinkAction* makeAction(Dict *dict, GString *baseURI)
{
Object obj1;
LinkAction* action=0;
// look for destination
if (!dict->lookup("Dest", &obj1)->isNull()) {
action = new LinkGoTo(&obj1);
// look for action
} else {
Object obj2;
obj1.free();
if (dict->lookup("A", &obj1)->isDict()) {
obj1.dictLookup("S", &obj2);
Object obj3,obj4;
// GoTo action
if (obj2.isName("GoTo")) {
obj1.dictLookup("D", &obj3);
action = new LinkGoTo(&obj3);
obj3.free();
// GoToR action
} else if (obj2.isName("GoToR")) {
obj1.dictLookup("F", &obj3);
obj1.dictLookup("D", &obj4);
action = new LinkGoToR(&obj3, &obj4);
obj3.free();
obj4.free();
// Launch action
} else if (obj2.isName("Launch")) {
action = new LinkLaunch(&obj1);
// URI action
} else if (obj2.isName("URI")) {
obj1.dictLookup("URI", &obj3);
action = new LinkURI(&obj3, baseURI);
obj3.free();
// Named action
} else if (obj2.isName("Named")) {
obj1.dictLookup("N", &obj3);
action = new LinkNamed(&obj3);
obj3.free();
// unknown action
} else if (obj2.isName()) {
action = new LinkUnknown(obj2.getName());
// action is missing or wrong type
} else {
//error(-1, "Bad annotation action");
action = NULL;
}
obj2.free();
}
}
obj1.free();
return action;
}
//****************************************************************
//****************************************************************
//****************************************************************